package com.github.cdisk.xtrans.component;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.github.cdisk.xtrans.annotiation.Attribute;
import com.github.cdisk.xtrans.annotiation.Component;
import com.github.cdisk.xtrans.core.BaseComponent;
import com.github.cdisk.xtrans.core.ConfigFile;
import com.github.cdisk.xtrans.core.GlobalVariables;
import com.github.cdisk.xtrans.exception.BuildException;
import com.github.cdisk.xtrans.exception.NoVariableNameException;

import jcifs.smb.NtlmPasswordAuthentication;
import jcifs.smb.SmbAuthException;
import jcifs.smb.SmbException;
import jcifs.smb.SmbFile;
import jcifs.smb.SmbFileInputStream;

/**
 * 文件上传
 * @author Cdisk
 *
 */
@Component(value="文件上传", icon="disk_upload.png", group="常用元件")
public class FileUpload extends BaseComponent {

	private static final Logger LOGGER = Logger.getLogger(FileUpload.class);

	/** 请求连接地址 */
	@Attribute(value="*请求地址", group="请求")
	protected String url = "";

	/** 要上传的文件所在的位置 */
	@Attribute(value="*上传文件", group="请求")
	protected String file = "";

	/** HEADER列表 */
	@Attribute(value="请求属性", group="请求")
	protected String requestProperty;

	/** 连接超时时间 */
	@Attribute(value="连接超时时间", group="请求")
	protected Integer connectTimeout = 30000;

	/** 读取超时时间 */
	@Attribute(value="读取超时时间", group="请求")
	protected Integer readTimeout = 30000;

	/** 请求返回参数的名称 */
	@Attribute(value="结果输出名称", group="请求")
	protected String resultName = "result";

	/** 请求参数字段 */
	@Attribute(value="请求发送的字段名称，换行分隔；不填代表所有字段。", group="请求")
	protected String[] params = null;

	/** 请求字符集类型 */
	@Attribute(value="请求字符集类型", group="请求")
	protected String charset = "utf-8";

	@Attribute(value="用户域", group="SMB共享")
	protected String smbdomain = ".";

	@Attribute(value="用户名", group="SMB共享")
	protected String smbuser = "guest";

	@Attribute(value="密码", group="SMB共享")
	protected String smbpassword = "";


	@Override
	public void build(ConfigFile config, GlobalVariables variables) throws BuildException {
		super.build(config, variables);

		try {
			url = variables.replaceVariables(url); // 替换变量
			if (StringUtils.isEmpty(url)) {
				throw new BuildException(this.getClass().getSimpleName() + "(" +  this.id + ")未找到[url]内容。");
			}
			resultName = variables.replaceVariables(resultName); // 替换变量
			if (StringUtils.isEmpty(resultName)) {
				throw new BuildException(this.getClass().getSimpleName() + "(" +  this.id + ")未找到[resultName]内容。");
			}

			resultName = variables.replaceVariables(resultName); // 替换变量
			if (StringUtils.isEmpty(resultName)) {
				throw new BuildException(this.getClass().getSimpleName() + "(" +  this.id + ")未找到[resultName]内容。");
			}

			requestProperty = variables.replaceVariables(requestProperty); // 替换变量
			charset = variables.replaceVariables(charset); // 替换变量
		} catch (NoVariableNameException e) {
			throw new BuildException(this.getClass().getSimpleName() + "(" +  this.id + ")" + e.getMessage());
		}
	}

	@Override
	public int execute(Map<String, Object> input) {
		HttpURLConnection conn = null;
		try {
			/* 替换传入变量 */
			String url = GlobalVariables.replaceInputVariables(this.url, input);
			String filePath = GlobalVariables.replaceInputVariables(this.file, input);

			String boundary = "---------" + ((int) (Math.random()*1000000000));
			String boundaryHeader = "--";
			String boundaryEnd = "\r\n";
			conn = (HttpURLConnection) new URL(url).openConnection();
			conn.setRequestMethod("POST");
			conn.setDoOutput(true);
			conn.setDoInput(true);
			conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("Charsert", charset);
            conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
			if (requestProperty != null && requestProperty.length() > 0) {
				/* 处理额外的请求属性。 */
				for (String property : requestProperty.split("\r?\n")) {
					String[] keyValue = property.split(":\\s?", 2);
					if (keyValue.length == 2) {
						conn.addRequestProperty(keyValue[0], keyValue[1]);
					}
				}
			}

			/* 上传文件 */
			DataOutputStream dos = new DataOutputStream(conn.getOutputStream());
			File file = new File(filePath);
            dos.writeBytes(boundaryHeader + boundary + boundaryEnd);
            dos.writeBytes("Content-Disposition: form-data; name=\"file\"; filename=\"" + file.getName() + "\"" + boundaryEnd);
            dos.writeBytes(boundaryEnd);
            DataInputStream in = new DataInputStream(getFileInputStream(filePath));
            int bytes = 0;
            byte[] bufferOut = new byte[2048];
            while ((bytes = in.read(bufferOut)) != -1) {
            	dos.write(bufferOut, 0, bytes);
            }
            dos.writeBytes(boundaryEnd);
            in.close();

            /* 提交参数 */
			for (String key : params) {
				Object value = GlobalVariables.getInputValue(key, input);
				String[] keySplit = key.split("\\.");
				key = keySplit[keySplit.length - 1];
				dos.writeBytes(boundaryHeader + boundary + boundaryEnd);
				dos.writeBytes("Content-Disposition: form-data; name=\"" + key + "\""+ boundaryEnd);
				dos.writeBytes(boundaryEnd);
				dos.writeBytes(String.valueOf(value));
				dos.writeBytes(boundaryEnd);
			}
            dos.writeBytes(boundaryHeader + boundary + boundaryHeader + boundaryEnd);
            dos.flush();

            /* 读取返回 */
			BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
			String result = "";
			String line = null;
			while ( (line = reader.readLine()) != null ) {
				result += line;
			}
			conn.disconnect();

			/* 返回结果输出处理 */
			Map<String, Object> __map = new HashMap<String, Object>();

			/* 执行成功处理 */
			if (this.success != null) {
				__map.put("$parent", input);
				__map.put(resultName, result);
				this.success.execute(__map);
			}
			LOGGER.info("执行请求:" + this.id + ", 执行成功.");
			return EXECUTE_SUCCESS;
		} catch (Exception e) {
			/* 执行错误处理 */
			Map<String, Object> __map = new HashMap<String, Object>();
			if (this.error != null) {
				__map.put("$errorMessage", e.getMessage());
				this.error.execute(input);
			}
			e.printStackTrace();

			LOGGER.info("执行请求:" + this.id + ", 执行错误, " + e.getClass().getSimpleName() + ": " + e.getMessage());
			return EXECUTE_ERROR;
		}
	}

	/**
	 * 获取文件输入流
	 * @param filePath
	 * @return
	 * @throws FileNotFoundException 
	 * @throws UnknownHostException 
	 * @throws MalformedURLException 
	 * @throws SmbException 
	 * @throws NoVariableNameException 
	 */
	private InputStream getFileInputStream(String filePath) throws FileNotFoundException, NoVariableNameException {
		InputStream in = null;
		if (filePath.startsWith("smb://")) {
			String smbdomain = this.variables.replaceVariables(this.smbdomain);
			String smbuser = this.variables.replaceVariables(this.smbuser);
			String smbpassword = this.variables.replaceVariables(this.smbpassword);
			try {
				NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication(smbdomain, smbuser, smbpassword);
		        SmbFile remoteFile = new SmbFile(filePath, auth);
		        if (!remoteFile.exists()) {
		        	throw new FileNotFoundException(filePath);
		        }
		        in = new SmbFileInputStream(remoteFile);
			} catch (UnknownHostException e) {
				throw new FileNotFoundException("共享文件地址错误。");
			} catch (MalformedURLException e) {
				throw new FileNotFoundException("共享文件地址错误。");
			} catch (SmbAuthException e) {
				throw new FileNotFoundException("共享账号密码错误。");
			} catch (SmbException e) {
				throw new FileNotFoundException("共享文件读取错误：" + e.getMessage());
			}
		} else {
			File file = new File(filePath);
			in = new FileInputStream(file);
		}
		return in;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getFile() {
		return file;
	}

	public void setFile(String file) {
		this.file = file;
	}

	public String getRequestProperty() {
		return requestProperty;
	}

	public void setRequestProperty(String requestProperty) {
		this.requestProperty = requestProperty;
	}

	public Integer getConnectTimeout() {
		return connectTimeout;
	}

	public void setConnectTimeout(Integer connectTimeout) {
		this.connectTimeout = connectTimeout;
	}

	public Integer getReadTimeout() {
		return readTimeout;
	}

	public void setReadTimeout(Integer readTimeout) {
		this.readTimeout = readTimeout;
	}

	public String getResultName() {
		return resultName;
	}

	public void setResultName(String resultName) {
		this.resultName = resultName;
	}

	public String[] getParams() {
		return params;
	}

	public void setParams(String[] params) {
		this.params = params;
	}

	public String getCharset() {
		return charset;
	}

	public void setCharset(String charset) {
		this.charset = charset;
	}

	public String getSmbdomain() {
		return smbdomain;
	}

	public void setSmbdomain(String smbdomain) {
		this.smbdomain = smbdomain;
	}

	public String getSmbuser() {
		return smbuser;
	}

	public void setSmbuser(String smbuser) {
		this.smbuser = smbuser;
	}

	public String getSmbpassword() {
		return smbpassword;
	}

	public void setSmbpassword(String smbpassword) {
		this.smbpassword = smbpassword;
	}

}
